<template>
  <div class=main :id=div_id>
    <canvas :id=canvas_id ref="canvasRef"></canvas>
  </div>
</template>

<script lang="ts" setup>
import { defineExpose, defineProps, getCurrentInstance, onMounted, onUnmounted, ref, watch } from 'vue'
import { v4 as uuidv4 } from 'uuid'

let canvas_id = uuidv4()
let div_id = uuidv4()

let resizeObserver: ResizeObserver | null = null;

const proxy = getCurrentInstance()

/**
 * 时间范围
 */
interface TimeRange {
  start: number,
  end: number,
}

class ViewRect {
  left: number;
  right: number;
  top: number;
  bottom: number;
  width: number;
  height: number;
  constructor(left: number, right: number, top: number, bottom: number) {
    this.left = left
    this.right = right
    this.top = top
    this.bottom = bottom
    this.width = right - left;
    this.height = bottom - top;
  }
  contains(x: number, y: number) {
    return x >= this.left && x <= this.right && y >= this.top && y <= this.bottom
  }
}

let contentRect: ViewRect = new ViewRect(10, 1000, 10, 500); // 视图区域
let timeAxisRect: ViewRect = new ViewRect(10, 1000, 10, 500); // 时间轴区域
let leftPadding: number = 10;
let rightPadding: number = 10;
let topPadding: number = 10;
let bottomPadding: number = 25;
let timeAxisHeight: number = 20;

let timeRange: TimeRange = {
  start: 0,
  end: 131072,
}

let showTimeRange: TimeRange = {
  start: 0,
  end: 131072,
}

let timeRangeList: TimeRange[] = [
  { start: 0, end: 16384 },
  { start: 16384, end: 3000 },
  { start: 32768, end: 4000 },
  { start: 49152, end: 60000 },
  { start: 65536, end: 81920 },
  { start: 81920, end: 98304 },
  { start: 98304, end: 110000 },
  { start: 114688, end: 131072 },
]

let maindiv = ref();
let painter: CanvasRenderingContext2D | null = null; //
let canvas = ref<HTMLCanvasElement | null>(null); // 画布

let timeMark: number = 5000;
let timeMarkShow: boolean = false;

let chooseShow: boolean = false;
let chooseIng = false;
let chooseBeginTime: number = 0;
let chooseEndTime: number = 0;
let mouseBeginX: number = 0; // 鼠标按下点
let mouseBeginY: number = 0; // 鼠标按下点
let mouseHoverX: number = 0; // 鼠标悬浮坐标点X
let mouseHoverY: number = 0;
let mouseEndX: number = 0; // 鼠标弹起点X
let mouseEndY: number = 0; // 鼠标弹起点X
let selectTime: boolean = false; //时间轴选择
let moveTime: boolean = false; //时间轴移动
let subLineShow: boolean = true;//辅助线显示功能开启
let subYLineShow: boolean = false;//时间辅助线显示
let nowTime: number = 0; //当前时间

let isPainting = false;

let setTimeRange = (newTimeRange: TimeRange) => {
  timeRange.start = newTimeRange.start;
  timeRange.end = newTimeRange.end;
  showTimeRange.start = newTimeRange.start;
  showTimeRange.end = newTimeRange.end;
  paint()
}

let setTimeRangeList = (newTimeRangeList: TimeRange[]) => {
  timeRangeList = newTimeRangeList
  paint()
}

let setTimeMark = (newTimeMark: number) => {
  timeMark = newTimeMark
  timeMarkShow = true
  paint()
}

const TimeToPx = (time: number) => {
  return (time - showTimeRange.start) / (showTimeRange.end - showTimeRange.start) * contentRect.width + leftPadding
}

// 重置画布
const resetRect = () => {
  const canvasElement = canvas.value;
  if (canvasElement) {
    canvasElement.width = document.getElementById(div_id)?.clientWidth as number
    canvasElement.height = document.getElementById(div_id)?.clientHeight as number

    // 全部归0
    contentRect = new ViewRect(leftPadding, canvasElement.width - rightPadding, topPadding, canvasElement.height - bottomPadding - timeAxisHeight)
    timeAxisRect = new ViewRect(contentRect.left, contentRect.right, contentRect.bottom, canvasElement.height - bottomPadding)
  }
  paint()
}

const drawTimeRange = () => {
  if (!painter) return;
  painter.save()
  painter.strokeStyle = 'green'
  painter.lineWidth = 1
  painter.beginPath()

  painter.rect(contentRect.left, contentRect.top, contentRect.width, contentRect.height)
  painter.clip()

  for (let i = 0; i < timeRangeList.length; i++) {
    const range = timeRangeList[i]
    const leftPx = TimeToPx(range.start);
    const rightPx = TimeToPx(range.end);

    painter.fillStyle = "green"
    painter.fillRect(leftPx, contentRect.top, rightPx - leftPx, contentRect.height)
  }
  painter.closePath()
  painter.stroke()
  painter.restore()
}

const drawTimeMark = () => {
  if (!painter) return;
  painter.save()
  painter.rect(contentRect.left, contentRect.top, contentRect.width, contentRect.height + timeAxisRect.height)
  painter.clip()
  painter.strokeStyle = 'blue'
  painter.lineWidth = 2
  painter.beginPath()
  const markPx = TimeToPx(timeMark);
  painter.moveTo(markPx, contentRect.top)
  painter.lineTo(markPx, timeAxisRect.bottom)
  painter.stroke()
  painter.closePath()
  painter.restore()
}

const measureText = (font: string, text: string) => {
  let spanDom = document.createElement('span')
  spanDom.style.margin = '0'
  spanDom.style.font = font
  spanDom.style.opacity = '0'
  spanDom.innerHTML = text
  document.body.append(spanDom)
  let sizeD: { width: number; height: number; } = { width: 0, height: 0 }
  sizeD.width = spanDom.offsetWidth
  sizeD.height = spanDom.offsetHeight / 2
  spanDom.remove()
  return sizeD
}

const drawTimeAxis = () => {
  if (!painter) return;
  painter.strokeStyle = '#f8fafa'
  painter.fillStyle = '#f8fafa'
  let tempText = new Date(showTimeRange.start * 1000).toLocaleString();
  let textWidth = painter.measureText(tempText).width;
  let halfwidth = textWidth / 2

  let cloumnNum = contentRect.width / (textWidth * 2 )
  cloumnNum = Math.floor(cloumnNum)
  let step = timeAxisRect.width / cloumnNum

  painter.lineWidth = 1
  painter.beginPath()

  painter.moveTo(timeAxisRect.left, timeAxisRect.top)
  painter.lineTo(timeAxisRect.right, timeAxisRect.top)

  for (let i = 0; i <= cloumnNum; i++) {
    //将utc时间戳转为本地时间 yyyy-MM-dd hh:mm:ss
    let timeText = new Date((showTimeRange.start + (showTimeRange.end - showTimeRange.start) / cloumnNum * i) * 1000).toLocaleString();
    let halfHeight = measureText(painter.font, timeText).height
    let heightOffset = 5
    if (halfHeight < timeAxisHeight) {
      heightOffset = halfHeight
    } else {
      heightOffset = (timeAxisHeight - halfHeight) / 2
    }

    let minLineCount = 5;
    for (let j = 1; j < minLineCount; j++){
      painter.moveTo(timeAxisRect.left + (i * step) + j * step/minLineCount, timeAxisRect.top)
      painter.lineTo(timeAxisRect.left + (i * step) + j * step/minLineCount, timeAxisRect.top + 2)
    }

    painter.moveTo(timeAxisRect.left + (i * step), timeAxisRect.top)
    painter.lineTo(timeAxisRect.left + (i * step), timeAxisRect.top + 4)

    if (i === 0) {
      painter.fillText(timeText, timeAxisRect.left + (i * step), timeAxisRect.top + halfHeight + heightOffset, painter.measureText(timeText).width)
    } else if (i === cloumnNum) {
      painter.fillText(timeText, timeAxisRect.left + (i * step) - textWidth, timeAxisRect.top + halfHeight + heightOffset, painter.measureText(timeText).width)
    }else{
      painter.fillText(timeText, timeAxisRect.left + (i * step) - halfwidth, timeAxisRect.top + halfHeight + heightOffset, painter.measureText(timeText).width)
    }
  }
  painter.closePath()
  painter.stroke()
  painter.fill()
}

const drawSelectTimeAxis = () => {
  if (!painter) return;
  painter.save()
  painter.fillStyle = 'rgba(73,114,248,0.5)'
  painter.fillRect(mouseBeginX, contentRect.top, mouseEndX - mouseBeginX, contentRect.height + timeAxisRect.height)
  painter.restore()
}


const drawSubLine = () => {
  if (!painter) return;
  painter.save()
  painter.setLineDash([5, 3]); // [实线长度, 间隔长度]设置虚线
  painter.strokeStyle = '#fafafa'
  painter.lineWidth = 1
  painter.beginPath()

  let showTime = new Date(nowTime * 1000).toLocaleString();
  let textWidth = painter.measureText(showTime).width;
  let textHeight = measureText(painter.font, showTime).height;
  let offsetX = 0;
  let offsetY = 0;
  if(mouseHoverX + textWidth > contentRect.right){
    offsetX = -textWidth;
  }
  if(mouseHoverY - textHeight < contentRect.top){
    offsetY = textHeight;
  }

  if (subYLineShow) {
    painter.moveTo(mouseHoverX, contentRect.top)
    painter.lineTo(mouseHoverX, contentRect.bottom)
    painter.fillText(showTime, mouseHoverX + offsetX + 2, mouseHoverY + offsetY, textWidth)
  }
  painter.stroke()
  painter.closePath()
  painter.restore()
}

const paint = () => {
  if (isPainting) return;
  isPainting = true;
  if (painter && canvas.value) {
    painter.clearRect(0, 0, canvas.value.width, canvas.value.height)
  }
  drawTimeRange()
  drawTimeAxis()
  if (selectTime) drawSelectTimeAxis()
  if (subLineShow) drawSubLine()
  drawTimeMark()
  isPainting = false;
}
// 封装的scrollTop
const scroll = () => {
  if (window.pageYOffset != null) { // ie9+ 高版本浏览器
    // 因为 window.pageYOffset 默认的是  0  所以这里需要判断
    return {
      left: window.pageXOffset,
      top: window.pageYOffset
    }
  } else if (document.compatMode === 'CSS1Compat') { // 标准浏览器   来判断有没有声明DTD
    return {
      left: document.documentElement.scrollLeft,
      top: document.documentElement.scrollTop
    }
  }
  return { // 未声明 DTD
    left: document.body.scrollLeft,
    top: document.body.scrollTop
  }
}
const canvasDown = (e: { clientX: number; clientY: number; button: number; }) => {
  if (!canvas.value) return;
  const canvasRect = canvas.value.getBoundingClientRect();
  mouseBeginX = e.clientX - canvasRect.x
  mouseBeginY = e.clientY - canvasRect.y
  mouseEndX = mouseBeginX
  mouseEndY = mouseBeginY
  if (contentRect.contains(mouseBeginX, mouseBeginY) || timeAxisRect.contains(mouseEndX, mouseEndY)) {
    if (e.button === 2) {
      selectTime = true
    } else if (e.button === 0) {
      moveTime = true
    }
  }
}
const canvasMove = (e: { clientX: number; clientY: number; }) => {
  if (!canvas.value) return;
  const canvasRect = canvas.value.getBoundingClientRect();
  mouseHoverX = e.clientX - canvasRect.x
  mouseHoverY = e.clientY - canvasRect.y
  mouseEndX = mouseHoverX
  mouseEndY = mouseHoverY

  if (subLineShow) {
    let wanthTime = Math.abs(showTimeRange.start - showTimeRange.end);
    nowTime = showTimeRange.start + ((mouseHoverX - leftPadding) / contentRect.width) * wanthTime;
  }

  if (contentRect.contains(mouseHoverX, mouseHoverY)) {
    subYLineShow = true;
  } else {
    subYLineShow = false;
  }

  if (chooseIng) {
    chooseEndTime = (mouseEndX - contentRect.left) / contentRect.width * (showTimeRange.end - showTimeRange.start) + showTimeRange.start
  }
  if (moveTime) {
    let moveVal = (showTimeRange.end - showTimeRange.start) / contentRect.width * (mouseBeginX - mouseEndX)
    if ((showTimeRange.start + moveVal) >= timeRange.start && (showTimeRange.end + moveVal) <= timeRange.end) {
      showTimeRange.start += moveVal
      showTimeRange.end += moveVal
    }
    mouseBeginX = mouseEndX
    mouseBeginY = mouseEndY
  }
  // if (selectTime || chooseIng || moveTime || subYLineShow)
  paint()
}
const canvasUp = (e: { clientX: number; clientY: number; }) => {
  if (!canvas.value) return;
  const canvasRect = canvas.value.getBoundingClientRect();
  mouseEndX = e.clientX - canvasRect.x
  mouseEndY = e.clientY - canvasRect.y
  if (chooseIng) {
    chooseEndTime = (mouseEndX - contentRect.left) / contentRect.width * (showTimeRange.end - showTimeRange.start) + showTimeRange.start
    if (chooseEndTime < chooseBeginTime) {
      let time = chooseEndTime
      chooseEndTime = chooseBeginTime
      chooseBeginTime = time
    }
    if (chooseEndTime === chooseBeginTime) {
      chooseShow = false
    } else {
      // 发送信号
      proxy?.emit('chooseTimeChange', chooseBeginTime, chooseEndTime)
    }
  }
  if (selectTime) {
    let beginTime = (mouseBeginX > mouseEndX ? mouseEndX : mouseBeginX - contentRect.left)
      / contentRect.width * (showTimeRange.end - showTimeRange.start) + showTimeRange.start
    let endTime = (mouseBeginX > mouseEndX ? mouseBeginX : mouseEndX - contentRect.left)
      / contentRect.width * (showTimeRange.end - showTimeRange.start) + showTimeRange.start
    if (beginTime != endTime) {
      showTimeRange.start = beginTime
      showTimeRange.end = endTime
    }
  }
  chooseIng = false
  selectTime = false
  moveTime = false
  paint()
}
const canvasDblClick = (e: { button: number; }) => {
  if (e.button === 0) {
    if(contentRect.contains(mouseHoverX, mouseHoverY)){
      proxy?.emit('clickTime', nowTime)// 发送信号
    }else if(timeAxisRect.contains(mouseHoverX, mouseHoverY)){
      showTimeRange.start = timeRange.start;
      showTimeRange.end = timeRange.end;
    }
    paint()
  }
}
const canvasWheel = (e: any) => {
  if (e.wheelDelta > 0 || e.detail < 0) {
    if (contentRect.contains(mouseBeginX, mouseBeginY) || timeAxisRect.contains(mouseBeginX, mouseBeginY)) {
      let changeVal = (showTimeRange.end - showTimeRange.start) / 10
      let changeLeftVal = (mouseHoverX - contentRect.left) / contentRect.width * changeVal
      let changeRightVal = changeVal - changeLeftVal
      showTimeRange.start += changeLeftVal
      showTimeRange.end -= changeRightVal
    }
  } else {
    if (contentRect.contains(mouseBeginX, mouseBeginY) || timeAxisRect.contains(mouseBeginX, mouseBeginY)) {
      let changeVal = (showTimeRange.end - showTimeRange.start) / 9
      let changeLeftVal = (mouseHoverX - contentRect.left) / contentRect.width * changeVal
      let changeRightVal = changeVal - changeLeftVal
      if (showTimeRange.start - changeLeftVal < timeRange.start) {
        showTimeRange.start = timeRange.start
      } else {
        showTimeRange.start -= changeLeftVal
      }
      if (showTimeRange.end + changeRightVal > timeRange.end) {
        showTimeRange.end = timeRange.end
      } else {
        showTimeRange.end += changeRightVal
      }
    }
  }
  paint()
}
const canvasOver = () => {
  // console.log('移入')
}
const canvasOut = (e: { clientX: number; clientY: number; }) => {
  if (chooseIng) {
    if (canvas.value) {
      mouseEndX = e.clientX - canvas.value.offsetLeft
      mouseEndY = e.clientY - canvas.value.offsetTop
    }
    chooseIng = false
  }
  selectTime = false
  moveTime = false
  subYLineShow = false
}

// 处理尺寸变化的回调函数
const handleResize = (entries: ResizeObserverEntry[]) => {
  for (const entry of entries) {
    if (entry.target === maindiv.value) {
      // 在这里可以添加处理逻辑，例如调整 canvas 尺寸或重新绘制内容
      if (canvas.value) {
        resetRect();
        // 调用绘图函数或其他操作
      }
    }
  }
};


onMounted(() => {
  maindiv.value = document.getElementById(div_id);
  canvas.value = document.getElementById(canvas_id) as HTMLCanvasElement;
  const canvasElement = canvas.value;
  if (!canvasElement) return;
  canvasElement.width = 200
  canvasElement.height = 100
  painter = canvasElement.getContext('2d')
  if (!painter) return;
  painter.fillStyle = 'rgb(133,155,238)'
  canvasElement.addEventListener('contextmenu', function (e) {
    e.preventDefault()
  })

  canvasElement.addEventListener('mousedown', canvasDown)
  canvasElement.addEventListener('mousemove', canvasMove)
  canvasElement.addEventListener('mouseup', canvasUp)
  canvasElement.addEventListener('dblclick', canvasDblClick)
  // canvasElement.addEventListener('mousewheel', canvasWheel)
  canvasElement.addEventListener('wheel', canvasWheel); //解决火狐浏览器绑定无效
  canvasElement.addEventListener('mouseover', canvasOver)
  canvasElement.addEventListener('mouseout', canvasOut)

  if (maindiv.value) {
    resizeObserver = new ResizeObserver(handleResize);
    resizeObserver.observe(maindiv.value);
  } else {
    console.warn('maindiv is not available');
  }
  resetRect();
})

onUnmounted(() => {

  // 移除监听器
  if (resizeObserver && maindiv.value) {
    resizeObserver.unobserve(maindiv.value);
    resizeObserver.disconnect();
  }
})

defineExpose({
  setTimeRange,//设置时间范围
  setTimeMark,//设置时间标记
  setTimeRangeList,//设置时间范围列表
  resetRect//调整大小
})

</script>

<style scoped>
.main {
  width: 100%;
  height: 100%;
  background: transparent;
}
</style>
